#pragma once

namespace _PARALLEL_SYSTEM_
{
	///////////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////
	class CParallelTask;
	class CParallelWorker;
	class JobBase
	{
	public:
		virtual ~JobBase(){ m_completionHandler = nullptr; }

	public:
		typedef std::function< void() > ParallelJobFunction;
		typedef std::function< void(JobBase* completed) > CompletionFunction;

		typedef std::list<JobBase*> LIST_JOBS;
		CompletionFunction	m_completionHandler;

	public:
		virtual void	AddJob( const ParallelJobFunction& job ){}

		virtual void	DoWork( CParallelWorker* worker = nullptr ) = 0;
		virtual void	PostJob( CParallelTask* task );
	};

	class Job : public JobBase
	{
	public:
		virtual ~Job() { }

	public:
		ParallelJobFunction	m_job;

	public:
		void			AddJob( const ParallelJobFunction& job ) override { m_job = job; }
		virtual void	DoWork( CParallelWorker* worker = nullptr ) override
		{ 
			if( m_job != nullptr )
				m_job();
		}
	};

	class DynamicJob : public JobBase
	{
	public:
		virtual ~DynamicJob() {}

	public:
		typedef std::list< ParallelJobFunction > PARALLEL_JOB_LIST;
		PARALLEL_JOB_LIST	m_Jobs;

		void	AddJob( const ParallelJobFunction& job ) override { m_Jobs.push_back(job); }
		void	DoWork( CParallelWorker* worker = nullptr ) override;
	};

	class ParallelJobEntry : public JobBase
	{
	public:
		ParallelJobEntry(){}
		~ParallelJobEntry(){}

	private:
		JobBase::LIST_JOBS m_Jobs;

	public:
		void	AddJob( JobBase* job );
		void	AddJob( ParallelJobFunction& job );
		void	DoWork( CParallelWorker* worker = nullptr ) override {}
		void	PostJob( CParallelTask* task ) override;
	};

	///////////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////
	class CParallelWorker : public CSimpleThread
	{
	public:
		CParallelWorker( CParallelTask* own_task, unsigned long id )
		{
			m_ownTask = own_task;
			m_worker_id = id;
		}
		
	private:
		CParallelTask*	m_ownTask;
		unsigned long	m_worker_id;

	private:
		virtual void __stdcall ThreadProc( LPVOID pParam ) override;

	public:
		unsigned long	GetThreadID();
	};

	class CParallelTask
	{
	public:
		CParallelTask();
		~CParallelTask();

	private:
		BYTE				m_task_id;

		typedef std::vector<CParallelWorker*> VECTOR_WORKERS;
		VECTOR_WORKERS		m_workers;

	private:
		CQueMT<JobBase*>*	m_queueJobs;
		friend class CParallelWorker;

	public:
		virtual void	Initialize( BYTE task_id, size_t worker_count );
		virtual void	Finalize();

	public:
		BYTE			GetTaskID(){ return m_task_id; }

	public:
		void			PostJob( JobBase* job );
	};

	///////////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////////
	#define STATIC_TASK_ID	(BYTE)0
	class CParallelSystem : public CSingletonT<CParallelSystem>
	{
	public:
		CParallelSystem(void);
		virtual ~CParallelSystem(void);

	private:
		typedef std::map<BYTE,CParallelTask*> MAP_TASKS;
		MAP_TASKS					m_tasks;

		CParallelTask*				static_task;

	private:
		typedef std::queue<JobBase*> QUEUE_COMPLETED_JOBS;
		QUEUE_COMPLETED_JOBS		m_CompletedJobs;
		CCriticalSectionBS			m_CompletedJobCS;

	public:
		void	Initialize();
		void	Finalize();

		bool	AddTask( CParallelTask* task );
		bool	CreateDBTask( BYTE task_id, size_t session_count );

	public:
		void	PostJob( BYTE task_id, JobBase* job );
		void	PostJob( JobBase* job );

		void	JobCompleted( JobBase* job );
	
	public:
		void	ProcessJobCompletion();
	};
};

#define PARALLEL_SYSTEM	_PARALLEL_SYSTEM_::CParallelSystem::GetSingleton()
